<--- %%NOBANNER%% --> resizen.sas
 BackForward

/*-------------------<-- Start of Description-->---------------------\
| create SAS dataset &outdata using LENGTH statement for numeric vars|
| that optimizes the variable to the fewest # of bytes needed to     |
| maintain the accuracy of the values contained in the variable;     |
|---------------------<-- End of Description-->----------------------|
|--------------------------------------------------------------------|
|------------<-- Start of Files or Arguments Needed-->---------------|
| Arguments neede:                                                   |
|    indata : input dataset name; Required;                          |
|    outdata: Output dataset name; Required;                         |
| Note: the output dataset name cannot be the same as input dataset; |
|---------------<-- End of Files Arguments Needed-->-----------------|
|--------------------------------------------------------------------|
|------------------<-- Start of Files Created-->---------------------|
| Example:                                                           |
|    data one ;                                                      |
|       format y x 11.6;                                             |
|       x=1.111; y=2.222;output;                                     |
|       x=11.1234; y=1.1;output;                                     |
|    run;                                                            |
|    %resizen(one); %prcnt(one); %print(one);                        |
| Usage: %resizen(indata, outdata);                                  |
\-------------------<-- End of Files Created-->---------------------*/
%macro resizen/parmbuff;
/*--------------------------------------------\
| Copy Right: Duo Zhou;                       |
| Created:  3-11-2002 9:42pm;                 |
| Purpose: Minimize numeric variables length; |
\--------------------------------------------*/
%local I sqzlength _VAR_NAMES_ _VAR_N_ _MAX_LEN_ _oriord_;
%let inbuff=%qscan(&syspbuff,1,%str((),));
%let outbuff=%qscan(&syspbuff,2,%str((),));
%let linesize = %SYSFUNC(GETOPTION(linesize));
%if (%index(%quote(&syspbuff),%quote(=))) %then %do;
   %if (%index(%quote(&inbuff),%quote(=))) %then %do;
      %if (%index(%quote(%upcase(%sysfunc(compress(%quote(&inbuff))))),%quote(INDATA=))) %then %do;
         %let indata=%qscan(&inbuff,2,%str(=));
         %if (%index(%quote(&outbuff),%quote(=))) %then %do;
            %if (not %index(%quote(%upcase(%sysfunc(compress(%quote(&outbuff))))),%quote(OUTDATA=))) %then %do;
               %put ==> Alert! Keyword parameter "%qscan(&outbuff,1,%str(=))" is not defined!;
            %end;
            %else %do;
               %let outdata=%qscan(&outbuff,2,%str(=));
            %end;
         %end;
         %else %do;
            %let outdata=&outbuff;
         %end;
      %end;
      %else %if (%index(%quote(%upcase(%sysfunc(compress(%quote(&inbuff))))),%quote(OUTDATA=))) %then %do;
         %let outdata=%qscan(&inbuff,2,%str(=));
         %if (%index(%quote(&outbuff),%quote(=))) %then %do;
            %if (not %index(%quote(%upcase(%sysfunc(compress(%quote(&outbuff))))),%quote(INDATA=))) %then %do;
               %put ==> Alert! Keyword parameter "%qscan(&outbuff,1,%str(=))" is not defined!;
            %end;
            %else %do;
               %let indata=%qscan(&outbuff,2,%str(=));
            %end;
         %end;
         %else %do;
            %let indata=&outbuff;
         %end;
      %end;
      %else %put ==> Alert! Keyword parameter "%qscan(&inbuff,1,%str(=))" is not defined!;
   %end;
   %else %if (%index(%quote(&outbuff),%quote(=))) %then %do;
      %if (%index(%quote(%upcase(%sysfunc(compress(%quote(&outbuff))))),%quote(INDATA=))) %then %do;
         %let indata=%qscan(&outbuff,2,%str(=));
         %let outdata=&inbuff;
      %end;
      %else %if (%index(%quote(%upcase(%sysfunc(compress(%quote(&outbuff))))),%quote(OUTDATA=))) %then %do;
         %let indata=&inbuff;
         %let outdata=%qscan(&outbuff,2,%str(=));
      %end;
      %else %do;
         %put ==> Alert! Keyword parameter "%qscan(&outbuff,1,%str(=))" is not defined!;
      %end;
   %end;
%end;
%else %do;
   %let indata=&inbuff;
   %let outdata=&outbuff;
%end;
%if (%quote(&indata) eq) %then %do;
   %put ==> Alert! No input dataset is to be resized!;
   %goto L9999;
%end;
%if (%quote(&outdata) eq) %then %do;
   %let outdata=&indata;
%end;
%put *=================================================* ;
%put | Beginning to resize numeric variables.          | ;
%put | Please be patient because this process may      | ;
%put | require a lot of execution time.                | ;
%put *=================================================* ;

%let _VAR_N_ = 0 ;

/*##########################################################*/
/* begin executable code                                    */
/*##########################################################*/
proc contents data=&indata memtype=data noprint out=_cntnts_; run;
proc sql noprint;
   select count(distinct name), max(length) into: _VAR_N_, :_MAX_LEN_
   from _cntnts_
   where type=1;

   select distinct name into: _VAR_NAMES_ separated by ", "
   from _cntnts_
   where type=1;

   select name into : _oriord_ separated by ' '
   from _cntnts_
   order by varnum;
quit;
%let best=Best%sysfunc(trimn(%sysfunc(left(&_MAX_LEN_))));
/* in case there are NO numeric vars in dataset,  */
/* stop further processing                        */
%if (%quote(&_VAR_N_) = %quote(0)) %then %do;
   %put *==================================* ;
   %put | ERROR from resizen:              | ;
   %put | No numeric variables in dataset. | ;
   %put | Execution terminating forthwith. | ;
   %put *==================================* ;
   %goto L9999 ;
%end ;

/* create macro vars containing variable name */
/* process variables of numeric type only     */
data _null_;
   set _cntnts_;
   if type = 1;
   var_no + 1;
   call symput( '_VAR_'||left( put( var_no, 5. )), name);
run;

/* Compute the minimum length and format for each numeric variable*/
proc sql noprint;
   select max(length(trim(left(put(&_VAR_1, &best..))))),
          max(length(trim(left(scan(put(&_VAR_1, &best..), 2, '.')))))
          %if &_VAR_N_ > 1 %then %do;
             %do _I_=2 %to &_VAR_N_;
                , max(length(trim(left(put(&&_VAR_&_I_, &best..))))), max(length(trim(left(scan(put(&&_VAR_&_I_, &best..), 2, '.')))))
             %end;
          %end;
          into: FMTL1, :FMTD1
          %if &_VAR_N_ > 1 %then %do;
             %do _I_=2 %to &_VAR_N_;
                , :FMTL&_I_, :FMTD&_I_
             %end;
          %end;
   from &indata;
quit;

/* initialize SQZLENTH global macro var */
%let SQZLENTH = LENGTH ; %let SQZFMT=FORMAT ;
%do I = 1 %to &_VAR_N_ ;
   %if (%quote(&&FMTD&I) ne ) %then %do;
      %let SQZLENTH = &SQZLENTH %qtrim(%left(&&_VAR_&I)) %trim(%left(%_max(%eval(&&FMTL&I+&&FMTD&I) 3)));
      %let SQZFMT=&SQZFMT %qtrim(%left(&&_VAR_&I)) %trim(%left(%eval(&&FMTL&I+&&FMTD&I))).%trim(%left(&&FMTD&I));
   %end;
   %else %do;
      %if (&&FMTL&I lt 3) %then %let SQZLENTH = &SQZLENTH %qtrim(%left( &&_VAR_&I )) %trim(%left(3));
      %else %if (&&FMTL&I gt 8) %then %let SQZLENTH = &SQZLENTH %qtrim(%left( &&_VAR_&I )) %trim(%left(8));
      %else %let SQZLENTH = &SQZLENTH %qtrim(%left( &&_VAR_&I )) %trim(%left(&&FMTL&I));
      %let SQZFMT=&SQZFMT %qtrim(%left(&&_VAR_&I)) %trim(%left(&&FMTL&I)).;
   %end;
%end;
/* apply SQZLENTH to incoming data, create output dataset */
data &outdata;
   retain &_oriord_;
   &SQZLENTH;
   &SQZFMT;
   set &indata;
run;
proc datasets nolist ; delete _cntnts_ _tmp_; run;
%L9999:
%mend resizen;